package com.fsnbigdata.web;


import com.fsnbigdata.domain.Log;
import com.fsnbigdata.domain.security.User;
import com.fsnbigdata.domain.security.UserTokenState;
import com.fsnbigdata.security.auth.BindEmailClass;
import com.fsnbigdata.security.auth.JwtAuthenticationRequest;
import com.fsnbigdata.security.auth.TokenHelper;
import com.fsnbigdata.service.api.LogService;
import com.fsnbigdata.service.api.UserService;
import com.fsnbigdata.service.impl.CustomUserDetailsService;
import com.fsnbigdata.util.*;
import com.fsnbigdata.util.Base64;
import com.fsnbigdata.util.DeviceProvider;
import com.fsnbigdata.util.Email;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mobile.device.Device;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.*;

/**
 * Created by fan.jin on 2017-05-10.
 */


@RestController
@RequestMapping( value = "/api/auth", produces = MediaType.APPLICATION_JSON_VALUE )
@Api(tags = "Token", description = "Token登录" )
public class AuthenticationController {
    //单终端登录，石伟志start
//    public static Map<String, String> loggedUser = new HashMap<String, String>(); //存储用户名和token
    //单终端登录，石伟志end

    @Autowired
    TokenHelper tokenHelper;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private DeviceProvider deviceProvider;

    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    /**
     * 发送验证码
     *
     * @param request
     * @return
     */
    @PostMapping("toSendEmail")
    public Map toSendEmail(
            @RequestParam("username")String username,
            @RequestParam("password")String password,
            HttpServletRequest request,
            ServletResponse servletResponse){
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setContentType("textml;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
//        response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "0");
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
        response.setHeader("Access-Control-Allow-Credentials", "true");
//        String username = request.getParameter("username");
        try {
            username = new String(Base64.decryptBASE64(username));
            password = new String(Base64.decryptBASE64(password));
        }catch (Exception e){
            System.out.println("解密失败");
        }
        Map<String, Object> map = new HashMap<String, Object>();
        User user = userService.findByUsername(username);
        if (user == null){
            map.put("code",201);
            map.put("msg","该账户不存在！");
            return map;
        }
//        if (!new BCryptPasswordEncoder().matches(password,user.getPassword())){
//            map.put("code",202);
//            map.put("msg","密码输入错误，请重新输入");
//            return map;
//        }
        if (user.getEmail().equals("")||user.getEmail() == null){
            map.put("code",203);
            map.put("msg","该账户未绑定邮箱！");
            return map;
        }
        String email = email = user.getEmail();
        // 验证码-获取随机数
        Integer email_code = (int) ((Math.random() * 9 + 1) * 100000);
        // email发送验证码
        Email email1 = new Email();
        try {
            email1.sendMail(email, email_code, "登录验证码");
            HttpSession session = request.getSession();
            session.setAttribute("codes",String.valueOf(email_code));
            session.setMaxInactiveInterval(60*5);
            System.out.println("------>>>> codes = " + session.getAttribute("codes"));
            map.put("code", 200);
            map.put("msg", "验证码发送成功，请登录邮箱查看");
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            map.put("code", 202);
            map.put("msg", "网络错误，验证码发送失败");
            return map;
        }
    }

    @RequestMapping(value = "/toBindSendEmail",method = RequestMethod.POST)
    public Map toBindSendEmail(
            @RequestParam("username")String username,
            @RequestParam("password")String password,
            @RequestParam("email")String email,
            HttpServletRequest request,
            ServletResponse servletResponse){
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setContentType("textml;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
//        response.setHeader("Access-Control-Allow-Origin", "http://localhost:8080");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "0");
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
        response.setHeader("Access-Control-Allow-Credentials", "true");
//        String username = request.getParameter("username");
        try {
            username = new String(Base64.decryptBASE64(username));
            password = new String(Base64.decryptBASE64(password));
        }catch (Exception e){
            System.out.println("解密失败");
        }
        Map<String, Object> map = new HashMap<String, Object>();
        if(username==null||"".equals(username)){
            map.put("code",0);
            map.put("msg","请输入用户名！");
            return map;
        }
        if(password==null||"".equals(password)){
            map.put("code",0);
            map.put("msg","请输入密码！");
            return map;
        }
        if(email==null||"".equals(email)){
            map.put("code",0);
            map.put("msg","请输入邮箱！");
            return map;
        }
        User user = userService.findByUsername(username);
        if (user == null){
            map.put("code",0);
            map.put("msg","该账户不存在！");
            return map;
        }
        if (!new BCryptPasswordEncoder().matches(password,user.getPassword())){
            map.put("code",0);
            map.put("msg","密码输入错误，请重新输入");
            return map;
        }
        if(!(user.getEmail()==null||"".equals(user.getEmail()))){
            map.put("code",0);
            map.put("msg","该帐号已绑定邮箱！");
            return map;
        }
        //判断邮箱有木有已经被绑定
        List list = userService.queryByEmail(email);
        if(list!=null&&list.size()!=0){
            map.put("code",0);
            map.put("msg","该邮箱已被绑定！");
            return map;
        }
        // 验证码-获取随机数
        Integer email_code = (int) ((Math.random() * 9 + 1) * 100000);
        // email发送验证码
        Email email1 = new Email();
        try {
            email1.sendMail(email, email_code, "登录验证码");
            HttpSession session = request.getSession();
            session.setAttribute("codes",String.valueOf(email_code));
            session.setMaxInactiveInterval(60*5);
            System.out.println("------>>>> codes = " + session.getAttribute("codes"));
            map.put("code", 1);
            map.put("msg", "验证码发送成功，请登录邮箱查看");
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            map.put("code", 0);
            map.put("msg", "网络错误，验证码发送失败");
            return map;
        }
    }

//    /**
//     * 验证邮箱验证码
//     *
//     * @param request
//     * @return
//     */
//    @PostMapping("validateCode")
//    public Map validateCode(@RequestParam("code")String code, HttpServletRequest request,ServletResponse servletResponse){
//        HttpServletResponse response = (HttpServletResponse) servletResponse;
//        response.setContentType("textml;charset=UTF-8");
//        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
//        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
//        response.setHeader("Access-Control-Max-Age", "0");
//        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
//        response.setHeader("Access-Control-Allow-Credentials", "true");
//        Map map = new HashMap();
//        String s_code = (String) request.getSession().getAttribute("codes");
//        if (s_code.equals(code)){
//            map.put("code",200);
//            map.put("msg","验证码输入正确！");
//            return map;
//        }
//        map.put("code",201);
//        map.put("msg","验证码输入错误！");
//        return map;
//    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(
            @Valid @RequestBody JwtAuthenticationRequest authenticationRequest,
            ServletResponse servletResponse,
            Device device,
            HttpServletRequest request
    ) throws AuthenticationException, IOException {
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setContentType("textml;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "0");
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        //base64解密处理
        try {
            authenticationRequest.setUsername(new String(Base64.decryptBASE64(authenticationRequest.getUsername())));
            authenticationRequest.setPassword(new String(Base64.decryptBASE64(authenticationRequest.getPassword())));
        }catch (Exception e){
            System.out.println("转换失败！");
        }
        String s_code =(String) request.getSession().getAttribute("codes");
        String code = authenticationRequest.getCode();
        if (s_code == null || s_code.equals("null")){
            // 未获取到验证码
            return ResponseEntity.badRequest().body(196);
        }else if (!Objects.equals(code,s_code)){
            // 验证码输入错误
            return ResponseEntity.badRequest().body(199);
        }
        if (authenticationRequest.getPassword() == null || Objects.equals(authenticationRequest.getPassword(),"")){
            return ResponseEntity.badRequest().body(197);
        }
        int status = userService.getUserStatus(authenticationRequest.getUsername(),authenticationRequest.getPassword());
        if(status!=200){
            return ResponseEntity.badRequest().body(status);
        }
        // Perform the security
        final Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        authenticationRequest.getUsername(),
                        authenticationRequest.getPassword()
                )
        );
        // Inject into security context
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // token creation
        User user = (User)authentication.getPrincipal();
        //单终端登录，杨鑫start
        PublicStaticData.userSign.put(user.getUsername(),new Date().getTime());
        //单终端登录，杨鑫end
        String jws = tokenHelper.generateToken( user.getUsername(), device);
        int expiresIn = tokenHelper.getExpiredIn(device);
        // Return the token
        try {
            Log log = new Log();

            log.setAddress(IPTOAddress.addr(IPTOAddress.getIP(request)));
            log.setClientInformation(IPTOAddress.getAddr(request));
            log.setIp(IPTOAddress.getIP(request));
            log.setType("login");
            log.setGenre("0");
            log.setUsername(user.getUsername());
            log.setDescription(user.getUsername()+"登录了系统！");
            log.setCreateTime(new Date());
            logService.save(log);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        //单终端登录，石伟志start
//        loggedUser.put(user.getUsername(), jws); // 记录用户名和token
        //单终端登录，石伟志end
        //清除session,保证一个验证码只能使用一次
        request.getSession().removeAttribute("codes");
        return ResponseEntity.ok(new UserTokenState(jws, expiresIn));
    }

    @RequestMapping(value = "/refresh", method = RequestMethod.POST)
    public ResponseEntity<?> refreshAuthenticationToken(
            HttpServletRequest request,
            HttpServletResponse response,
            Principal principal
            ) {

        String authToken = tokenHelper.getToken( request );

        Device device = deviceProvider.getCurrentDevice(request);

        if (authToken != null && principal != null) {
            //单终端登录，石伟志start
//            try { // 对比 token
//                String username = principal.getName();
//                String oldToken = loggedUser.get(username);
//                if (oldToken.equals(authToken)) {
//                    // user ok
//                } else {
//                    throw new Exception();
//                }
//            } catch (Exception e) {
//                return new ResponseEntity<String>("Unauthorized", HttpStatus.UNAUTHORIZED);
//            }
            //单终端登录，石伟志end
            // TODO check user password last update
            String refreshedToken = tokenHelper.refreshToken(authToken, device);
            int expiresIn = tokenHelper.getExpiredIn(device);
            //单终端登录，石伟志start
//            loggedUser.put(principal.getName(), refreshedToken);
            //单终端登录，石伟志end
            return ResponseEntity.ok(new UserTokenState(refreshedToken, expiresIn));
        } else {
            UserTokenState userTokenState = new UserTokenState();
            return ResponseEntity.accepted().body(userTokenState);
        }
    }

    @RequestMapping(value = "/bindEmail",method = RequestMethod.POST)
    public ResponseEntity<?> bindEmail(
            @Valid @RequestBody BindEmailClass bindEmailClass,
            HttpServletRequest request,
            ServletResponse servletResponse){
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.setContentType("textml;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "0");
        response.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        Map map = new HashMap();
        if(bindEmailClass.getUsername()==null||"".equals(bindEmailClass.getUsername())){
            map.put("status",0);
            map.put("msg","请输入用户名！");
            return ResponseEntity.badRequest().body(map);
        }
        if(bindEmailClass.getPassword()==null||"".equals(bindEmailClass.getPassword())){
            map.put("status",0);
            map.put("msg","请输入密码！");
            return ResponseEntity.badRequest().body(map);
        }
        if(bindEmailClass.getEmail()==null||"".equals(bindEmailClass.getEmail())){
            map.put("status",0);
            map.put("msg","请输入邮箱！");
            return ResponseEntity.badRequest().body(map);
        }
        if(!(bindEmailClass.getEmail().matches("^[a-z0-9]+([._\\\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$"))){
            map.put("status",0);
            map.put("msg","邮箱格式不正确！");
            return ResponseEntity.badRequest().body(map);
        }
        if(bindEmailClass.getCode()==null||"".equals(bindEmailClass.getCode())){
            map.put("status",0);
            map.put("msg","请输入验证码！");
            return ResponseEntity.badRequest().body(map);
        }
        //base64解密处理
        try {
            bindEmailClass.setUsername(new String(Base64.decryptBASE64(bindEmailClass.getUsername())));
            bindEmailClass.setPassword(new String(Base64.decryptBASE64(bindEmailClass.getPassword())));
        }catch (Exception e){
            System.out.println("转换失败！");
        }
        User user = userService.findByUsername(bindEmailClass.getUsername());
        if(user==null){
            map.put("status",0);
            map.put("msg","用户不存在！");
            return ResponseEntity.badRequest().body(map);
        }
        if(user.getEmail()!=null&&!("".equals(user.getEmail()))){
            map.put("status",0);
            map.put("msg","该用户已绑定邮箱！");
            return ResponseEntity.badRequest().body(map);
        }
        List list = userService.queryByEmail(bindEmailClass.getEmail());
        if(!(list==null||list.size()==0)){
            map.put("status",0);
            map.put("msg","该邮箱已被绑定！");
            return ResponseEntity.badRequest().body(map);
        }
        //对比密码
        if(!new BCryptPasswordEncoder().matches(bindEmailClass.getPassword(),user.getPassword())){
            map.put("status",0);
            map.put("msg","密码错误！");
            return ResponseEntity.badRequest().body(map);
        }
        Object s_code = request.getSession().getAttribute("codes");
        if(s_code==null||"".equals(""+s_code)){
            map.put("status",0);
            map.put("msg","未获取到验证码！");
            return ResponseEntity.badRequest().body(map);
        }
        if(bindEmailClass.getCode()==null||"".equals(bindEmailClass.getCode())){
            map.put("status",0);
            map.put("msg","请输入验证码！");
            return ResponseEntity.badRequest().body(map);
        }
        if(!Objects.equals(s_code,bindEmailClass.getCode())){
            map.put("status",0);
            map.put("msg","验证码输入错误！");
            return ResponseEntity.badRequest().body(map);
        }
        user.setEmail(bindEmailClass.getEmail());
        //保存是否有返回值，有表示成功，没有表示未知错误
        if(userService.save(user)==null){
            map.put("status",0);
            map.put("msg","邮箱绑定出现未知错误！");
            return ResponseEntity.badRequest().body(map);
        };
        map.put("status",1);
        map.put("msg","邮箱绑定成功！");
        request.getSession().removeAttribute("codes");
        return ResponseEntity.ok(map);
    }

    @RequestMapping(value = "/loginOut", method = RequestMethod.POST)
    @ApiOperation(value = "",notes = "loginOut")
    public Map loginOut(HttpServletRequest request) throws Exception {
        String authToken =tokenHelper.getToken(request);
        String username = tokenHelper.getUsernameFromToken(authToken);
        Map map = new HashMap();
        if(authToken!=null&& username!=null){
            Class<?> clazz = AuthenticationController.class;
            Method method = clazz.getMethod("loginOut", ReflectUtil.getPeremeterClass(clazz, "loginOut"));
            ReflectUtil.setAnnotationOn(method,username+"退出了系统!："+username);
            map.put("status",0);
        }else {
            map.put("status",1);
        }
        return map;
    }

}
